<?php

namespace youmbe\phpword\widgets;

use Yii;
use yii\helpers\ArrayHelper;
use yii\base\InvalidConfigException;
use yii\base\InvalidParamException;
use yii\i18n\Formatter;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpWord\PhpWord;
use PhpOffice\PhpWord\Style\ListItem as ListItemStyle;
use PhpOffice\Common\Font;
use PhpOffice\PhpWord\SimpleType\LineSpacingRule;
use PhpOffice\PhpWord\SimpleType\TextAlignment;
use PhpOffice\PhpWord\SimpleType\Jc;
use PhpOffice\PhpWord\Style\TablePosition;
use youmbe\phpword\RecursionListModelInterface;
use PhpOffice\PhpWord\Shared\Converter;
use PhpOffice\PhpWord\Shared\Html as PhpWordHtml;
use youmbe\phpword\widgets\TableView;
use youmbe\phpword\widgets\GridView;
use PhpOffice\PhpSpreadsheet\Style\Font as FontStyle;
use PhpOffice\PhpWord\SimpleType\TblWidth as TblWidthSimpleType;
use PhpOffice\PhpWord\SimpleType\JcTable;
use yii\data\ActiveDataProvider;

/**
 * Word Widget for generate Word File or for load Word File.
 *
 * @author emhome <emhome@163.com>
 * @since 1.0
 */
class SectionMutilListView extends \youmbe\phpword\base\Widget {

    /**
     * @var array|Closure the HTML attributes for the container of the rendering result of each data model.
     * This can be either an array specifying the common HTML attributes for rendering each data item,
     * or an anonymous function that returns an array of the HTML attributes. The anonymous function will be
     * called once for every data model returned by [[dataProvider]].
     * The "tag" element specifies the tag name of the container element and defaults to "div".
     * If "tag" is false, it means no container element will be rendered.
     *
     * If this property is specified as an anonymous function, it should have the following signature:
     *
     * ```php
     * function ($model, $key, $index, $widget)
     * ```
     *
     * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered.
     */
    public $itemOptions = [];

    /**
     * @var string|callable the name of the view for rendering each data item, or a callback (e.g. an anonymous function)
     * for rendering each data item. If it specifies a view name, the following variables will
     * be available in the view:
     *
     * - `$model`: mixed, the data model
     * - `$key`: mixed, the key value associated with the data item
     * - `$index`: integer, the zero-based index of the data item in the items array returned by [[dataProvider]].
     * - `$widget`: ListView, this widget instance
     *
     * Note that the view name is resolved into the view file by the current context of the [[view]] object.
     *
     * If this property is specified as a callback, it should have the following signature:
     *
     * ```php
     * function ($model, $key, $index, $widget)
     * ```
     */
    public $itemView;

    /**
     * @var array additional parameters to be passed to [[itemView]] when it is being rendered.
     * This property is used only when [[itemView]] is a string representing a view name.
     */
    public $viewParams = [];

    /**
     * @var string the HTML code to be displayed between any two consecutive items.
     */
    public $separator = "\n";

    /**
     * @var Closure an anonymous function that is called once BEFORE rendering each data model.
     * It should have the following signature:
     *
     * ```php
     * function ($model, $key, $index, $widget)
     * ```
     *
     * - `$model`: the current data model being rendered
     * - `$key`: the key value associated with the current data model
     * - `$index`: the zero-based index of the data model in the model array returned by [[dataProvider]]
     * - `$widget`: the ListView object
     *
     * The return result of the function will be rendered directly.
     * Note: If the function returns `null`, nothing will be rendered before the item.
     * @see renderBeforeItem
     * @since 2.0.11
     */
    public $beforeItem;

    /**
     * @var Closure an anonymous function that is called once AFTER rendering each data model.
     *
     * It should have the same signature as [[beforeItem]].
     *
     * The return result of the function will be rendered directly.
     * Note: If the function returns `null`, nothing will be rendered after the item.
     * @see renderAfterItem
     * @since 2.0.11
     */
    public $afterItem;

    /**
     * @var Closure an anonymous function that is called once AFTER rendering each data model.
     *
     * It should have the same signature as [[beforeItem]].
     *
     * The return result of the function will be rendered directly.
     * Note: If the function returns `null`, nothing will be rendered after the item.
     * @see renderAfterItem
     * @since 2.0.11
     */
    public $childFieldName = 'children';
    public $level = 0;
    public $listStyle;
    public $useNumberTitle = true;
    public $dataColumnClass;
    public $model;

    public function init() {
        parent::init();
        if ($this->model === null) {
            throw new InvalidConfigException('The "model" property must be set.');
        }
    }

    /**
     * @return ActiveDataProvider
     */
    public function getDataProvider() {
        return new ActiveDataProvider([
            'query' => $this->model->getContents(),
            'pagination' => false
        ]);
    }

    public function run() {
        parent::run();
        $models = $this->dataProvider->getModels();
        $this->renderListItems($models, $this->getListNumberingStyle(), $this->level, $this->useNumberTitle);
    }

    public function getListNumberingStyle() {
        if ($this->listStyle) {
            return $this->listStyle;
        }
        return $this->registerNumberingStyle($this->section);
    }

    /**
     * Renders all data models.
     * @return string the rendering result
     */
    public function renderListItems($models, $listStyle, $level = 0, $useNumberTitle = false) {
        foreach (array_values($models) as $index => $model) {
            $this->renderBeforeItem($model, $index);
            $this->renderItem($model, $index, $listStyle, $level, $useNumberTitle);
            $this->renderAfterItem($model, $index);
        }
    }

    /**
     * Calls [[beforeItem]] closure, returns execution result.
     * If [[beforeItem]] is not a closure, `null` will be returned.
     *
     * @param mixed $model the data model to be rendered
     * @param mixed $key the key value associated with the data model
     * @param int $index the zero-based index of the data model in the model array returned by [[dataProvider]].
     * @return string|null [[beforeItem]] call result or `null` when [[beforeItem]] is not a closure
     * @see beforeItem
     * @since 2.0.11
     */
    protected function renderBeforeItem($model, $index) {
        if ($this->beforeItem instanceof Closure) {
            call_user_func($this->beforeItem, $model, $index, $this);
        }
    }

    /**
     * Calls [[afterItem]] closure, returns execution result.
     * If [[afterItem]] is not a closure, `null` will be returned.
     *
     * @param mixed $model the data model to be rendered
     * @param mixed $key the key value associated with the data model
     * @param int $index the zero-based index of the data model in the model array returned by [[dataProvider]].
     * @return string|null [[afterItem]] call result or `null` when [[afterItem]] is not a closure
     * @see afterItem
     * @since 2.0.11
     */
    protected function renderAfterItem($model, $index) {
        if ($this->afterItem instanceof Closure) {
            call_user_func($this->afterItem, $model, $index, $this);
        }
    }

    /**
     * Creates column objects and initializes them.
     * @param \common\logics\word\contract\ContractPageTexts $model
     */
    protected function renderModelColumn($model, $index) {
        $contents = $this->formatModelContent($model);
        if (empty($contents)) {
            return;
        }
        foreach ($contents as $content) {
            $textRun = $this->section->addTextRun();
            $this->dumpRunText($textRun, $content);
        }
        /* @var $column DataColumn */
//        $column = Yii::createObject(array_merge([
//            'class' => $this->dataColumnClass ?: DataColumn::className(),
//            'grid' => $this,
//        ], [
//            'attribute' => 'content',
//            'value' => function($model) {
//                return $model->getContentHtml(['tag' => null]);
//            },
//        ]));
//        $data = $column->renderDataCell($model, $index);
//        if (!$data) {
//            return;
//        }
//        $this->section->addPreserveText($data);
    }

    /**
     * Renders a single data model.
     * @param mixed $model the data model to be rendered
     * @param mixed $key the key value associated with the data model
     * @param int $index the zero-based index of the data model in the model array returned by [[dataProvider]].
     * @return string the rendering result
     */
    public function renderItem($model, $index, $listStyle, $level, $useNumberTitle) {
        if (!$model instanceof RecursionListModelInterface) {
            return;
        }
        $title = $model->getListTitle();
        $childrenLevel = $level;
        if ($title) {
            if (!$level && $useNumberTitle) {
                $this->section->addTitle($title, 1);
            } else {
                $titleArray = $this->formatModelContent($model, $title);
                $styles = static::getMultiLevelStyle($level);
                $listItemRun = $this->section->addListItemRun($level, $listStyle, $styles['paragraph']);
                foreach ($titleArray as $t) {
                    $textRun = $listItemRun->addTextRun();
                    $this->dumpRunText($textRun, $t, $styles);
                }
            }
            $childrenLevel ++;
        }
        $this->renderModelColumn($model, $index);
        $items = ArrayHelper::getValue($model, $this->childFieldName, []);
        if (!empty($items)) {
            $this->renderListItems($items, $listStyle, $childrenLevel, $useNumberTitle);
        }
    }

    /**
     * 
     * @param \yii\base\Model $model
     * @param mixed $content
     * @return type
     */
    public function formatModelContent($model, $content = null) {
        $formatter = new \youmbe\phpword\models\ContentModel();
        $formatter->model = $model;
        $formatter->setAttributes($model->attributes);
        if ($content) {
            $formatter->content = $content;
        }
        return $formatter->save();
    }

    /**
     * 
     * @param \PhpOffice\PhpWord\Element\TextRun $textRun
     * @param array|string $data
     * @param array $styles
     */
    public function dumpRunText($textRun, $data, $styles = []) {
        if (empty($data)) {
            return;
        }
        $fontStyle = ArrayHelper::getValue($styles, 'fontStyle');
        if (is_string($data)) {
            $textRun->addText($data, $fontStyle);
        }
        if (!is_array($data)) {
            return;
        }
        foreach ($data as $text) {
            $_fontStyle = $fontStyle;
            if (is_array($text)) {
                $_fontStyle = ArrayHelper::getValue($text, 'style');
                $text = ArrayHelper::getValue($text, 'text');
            }
            $text = preg_replace("/(s|&nbsp;|　|xc2xa0)/", " ", strip_tags($text));
            $textRun->addText($text, $_fontStyle);
        }
    }

    public static function getMultiLevelStyle($level = null, $name = null) {
        $listStyles = static::getListStyles();
        if ($level === null) {
            return ArrayHelper::getColumn($listStyles, 'list');
        }
        $styles = ArrayHelper::getValue($listStyles, $level);
        if ($name) {
            return ArrayHelper::getValue($styles, $name);
        }
        return $styles;
    }

    public static function getListStyles() {
        return [
            [
                'font' => [
                    'name' => '黑体',
                    'size' => 14,
                ],
                'list' => [
                    'pStyle' => 'Heading1',
                    'format' => 'decimal', //bullet|decimal|upperRoman|lowerRoman|upperLetter|lowerLetter
                    'text' => '%1.', // Numbering level text e.g. %1 for nonbullet or bullet character.
                    'left' => 0, //缩进
                    'hanging' => 0, //悬挂缩进
//                    'hint' => 'eastAsia', //编号文本类型 Font content type, default, eastAsia, or cs.
                    'tabPos' => 0, //制表符位置
                    'suffix' => 'nothing', //Content between numbering symbol and paragraph text tab|space|nothing.
                    'alignment ' => \PhpOffice\PhpWord\SimpleType\Jc::BOTH,
                    'font' => '黑体',
//                    'restart' => true, //重新编号
//                    'start' => '', //编号
                ],
                'paragraph' => [
                    'indentation' => [
                        'firstLine' => Font::pointSizeToTwips(28),
                    ],
                    'spacing' => Font::pointSizeToTwips(24),
                    'spacingLineRule' => LineSpacingRule::EXACT,
                    'spaceBefore' => Font::pointSizeToTwips(14),
                    'spaceAfter' => Font::pointSizeToTwips(14),
                ]
            ],
            [
                'font' => null,
                'list' => [
                    'pStyle' => 'Heading2',
                    'format' => 'decimal', //bullet|decimal|upperRoman|lowerRoman|upperLetter|lowerLetter
                    'text' => '%1.%2.', // Numbering level text e.g. %1 for nonbullet or bullet character.
                    'left' => 0, //缩进
                    'hanging' => 0, //悬挂缩进
                    'tabPos' => 0, //制表符位置
                    'suffix' => 'nothing', //Content between numbering symbol and paragraph text tab|space|nothing.
                ],
                'paragraph' => [
                    'indentation' => [
                        'firstLine' => Font::pointSizeToTwips(28),
                    ],
                    'spacing' => Font::pointSizeToTwips(24),
                    'spacingLineRule' => LineSpacingRule::EXACT,
                ]
            ],
            [
                'font' => null,
                'list' => [
                    'pStyle' => 'Heading3',
                    'format' => 'decimal', //bullet|decimal|upperRoman|lowerRoman|upperLetter|lowerLetter
                    'text' => '%1.%2.%3.', // Numbering level text e.g. %1 for nonbullet or bullet character.
                    'left' => 0, //缩进
                    'hanging' => 0, //悬挂缩进
                    'tabPos' => 0, //制表符位置
                    'suffix' => 'nothing', //Content between numbering symbol and paragraph text tab|space|nothing.
                ],
                'paragraph' => [
                    'indentation' => [
                        'firstLine' => Font::pointSizeToTwips(28),
                    ],
                    'spacing' => Font::pointSizeToTwips(24),
                    'spacingLineRule' => LineSpacingRule::EXACT,
                ]
            ]
        ];
    }

    /**
     * 输出内容
     * @param \PhpOffice\PhpWord\Element\Section $section
     * @param array $name
     */
    public function registerNumberingStyle($section, $name = null) {
        $objectWord = $this->view;
        if (!$name) {
            $id = $section->getSectionId();
            $name = 'MultiLevel' . $id;
        } else {
            $objectWord->addTitleStyle($this->level, [
                'name' => '黑体',
                'size' => 14,
            ], [
                'numStyle' => $name,
                'numLevel' => 0,
                'indentation' => [
                    'firstLine' => Font::pointSizeToTwips(28),
                ],
                'spacing' => Font::pointSizeToTwips(24),
                'spacingLineRule' => LineSpacingRule::EXACT,
                'spaceBefore' => Font::pointSizeToTwips(14),
                'spaceAfter' => Font::pointSizeToTwips(14),
            ]);
        }
        $levels = static::getMultiLevelStyle();
        $objectWord->addNumberingStyle($name, [
            'type' => 'multilevel',
            'levels' => $levels,
        ]);
        return $name;
    }

    public function renderTable($model, $method) {
        GridView::widget([
            'options' => [
                'unit' => TblWidthSimpleType::PERCENT,
            ],
            'paragraphStyle' => [
                'indentation' => [
                    'firstLine' => 0,
                ],
                'indent' => 0,
                'lineHeight' => 1,
            ],
            'section' => $section,
            'dataProvider' => new \yii\data\ActiveDataProvider([
                'query' => $this->model->getDevices(),
                'pagination' => false
            ]),
            'columns' => [
                [
                    'attribute' => 'name',
                    'headerOptions' => [
                        'width' => 15,
                    ],
                ],
                [
                    'attribute' => 'spec',
                    'headerOptions' => [
                        'width' => 30,
                    ],
                ],
                [
                    'attribute' => 'unit',
                    'headerOptions' => [
                        'width' => 10,
                        'align' => 'right',
                    ],
                    'contentOptions' => [
                        'align' => 'right'
                    ]
                ],
                [
                    'attribute' => 'num',
                    'headerOptions' => [
                        'width' => 10,
                        'align' => 'right',
                    ],
                    'contentOptions' => [
                        'align' => 'right'
                    ]
                ],
                [
                    'attribute' => 'price',
                    'headerOptions' => [
                        'width' => 15,
                        'align' => 'right',
                    ],
                    'contentOptions' => [
                        'align' => 'right'
                    ]
                ],
                [
                    'attribute' => 'amount',
                    'headerOptions' => [
                        'width' => 15,
                        'align' => 'right',
                    ],
                    'contentOptions' => [
                        'align' => 'right'
                    ]
                ],
            ],
        ]);
    }

}
